package com.wgo.precise.client.ui.controller;

import java.lang.reflect.UndeclaredThrowableException;
import java.net.URL;
import java.util.Iterator;
import java.util.List;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.progress.IProgressService;

import remato.common.domain.helpers.Nameable;
import remato.domain.common.RequirementSystem;

import com.wgo.bpot.common.transport.exception.RematoException;
import com.wgo.bpot.common.transport.exception.StaleDataException;
import com.wgo.bpot.domain.common.CommandHistory;
import com.wgo.bpot.server.persist.Persistent;
import com.wgo.precise.client.common.session.PreciseSession;
import com.wgo.precise.client.ui.controller.exceptions.RematoClientException;
import com.wgo.precise.client.ui.model.NodeChild;
import com.wgo.precise.client.ui.model.NodeParent;
import com.wgo.precise.client.ui.model.WrapperFactory;
import com.wgo.precise.client.ui.model.facade.IModelPersistable;
import com.wgo.precise.client.ui.model.facade.INodeChild;
import com.wgo.precise.client.ui.model.facade.ModelSystem;
import com.wgo.precise.client.ui.view.util.ReflectionHelper;
import com.wgo.precise.client.ui.view.util.ReflectionRunnableWithProgress;

/**
 * This class holds information concerning a user session. It is instansiated 
 * once per login and is disposed at logout.
 * 
 * @author Petter L. H. Eide <petter@eide.biz>
 * @version $Id: ClientSession.java,v 1.3 2006-02-06 09:29:08 petterei Exp $
 */
public class ClientSession {
    
    private WrapperFactory wrapperFactory;
    private PreciseSession session = null;
    
    @SuppressWarnings("unchecked")
	public ClientSession(PreciseSession session) {
    	this.session = session;
//    	wrapperFactory = new WrapperFactory(session.<RequirementSystem>getRootSystem());
    	wrapperFactory = new WrapperFactory(session.getRootSystem(RequirementSystem.class));
    	
//    	for ( Persistent rootSystem : this.session.getRootSystems() ) {
//			RequirementPlugin.log(new ModelStatus(IStatus.INFO , "RootSystemType: " + rootSystem.getClass().getInterfaces()[0] ));
//    	}
    	
    	Class<? extends Nameable> optionalSystemType;
		try {
			optionalSystemType = (Class<? extends Nameable>) Class.forName("remato.domain.common.UseCaseSystem");
			RequirementPlugin.log(new ModelStatus(IStatus.INFO , "Optional system available"));
		} catch (ClassNotFoundException e) {
			RequirementPlugin.log(new ModelStatus(IStatus.INFO , "No optional system available"));
			return;
		}
//		try {
//			optionalSystemType = (Class<? extends Nameable>) getClass().getClassLoader().loadClass("remato.domain.common.UseCaseSystem");
//			RequirementPlugin.log(new ModelStatus(IStatus.INFO , "Optional system available"));
//		} catch (ClassNotFoundException e) {
//			RequirementPlugin.log(new ModelStatus(IStatus.INFO , "No optional system available 2"));
//		}
    	Nameable optionalDomainSystem = session.getRootSystem(optionalSystemType);
    	if (null == optionalDomainSystem) {
			RequirementPlugin.log(new ModelStatus(IStatus.ERROR , "Optional system seems to be available, but no optional system was retrieved from server."));
			return;
    	}
    	Class<? extends IModelPersistable> optionalSystemTypeWrapperType;
    	Class<ModelSystem<? extends IModelPersistable>> optionalSystemWrapperType;
		try {
			optionalSystemWrapperType = (Class<ModelSystem<? extends IModelPersistable>>) Class.forName("com.wgo.precise.client.ui.model.UseCaseSystemWrapper");
			optionalSystemTypeWrapperType = (Class<? extends IModelPersistable>) Class.forName("com.wgo.precise.client.ui.model.UseCaseDefinitionWrapper");
		} catch (ClassNotFoundException e) {
			RequirementPlugin.log(new ModelStatus(IStatus.ERROR , "Inconsistent optional system available only partiatially available.",e));
			return;
		}
    	wrapperFactory.addRootConceptSystem(optionalSystemTypeWrapperType, optionalSystemWrapperType, optionalDomainSystem);
    }

    public void dispose(){
    	wrapperFactory = null;
        session = null;
    }
    
	public WrapperFactory getWrapperFactory() {
		return wrapperFactory;
	}

	public boolean isDirty() { 
		if (getDomainSession() != null) {
			return getDomainSession().isAnyDirty();
		}
		return false;
	}

	public IModelPersistable getModelItem(Long identifier) {
        IModelPersistable wrapper = null;
        Iterator<ModelSystem<?>> systemIterator = wrapperFactory.getSystems().iterator();
        while (systemIterator.hasNext() && null == wrapper) {
        	Iterator<? extends INodeChild> childrenIterator = systemIterator.next().getChildren().iterator();
            while (childrenIterator.hasNext() && null == wrapper) {
        		wrapper = getModelItem(childrenIterator.next(), identifier);
        	}
        }
        return wrapper;
	}
	
    @SuppressWarnings("unchecked")
	private INodeChild getModelItem( INodeChild child, Long identifier ) {
        INodeChild childMatch = null;
        if (child.getIdentifier().equals(identifier)) {
            childMatch = child;
        } else if (child instanceof NodeParent) {
            for ( Object parentChild : ((NodeParent)child).getChildren() ) {
                childMatch = getModelItem((NodeChild)parentChild, identifier);
                if (childMatch != null) {
                    return childMatch;
                }
            }
        }
        return childMatch;
    }

	public PreciseSession getDomainSession() {
		return session;
	}

	public synchronized void saveAll() {
        IRunnableWithProgress op = new ReflectionRunnableWithProgress("Saving all changes.", ReflectionHelper.retrieveMethod(getClass().getSimpleName(), session.getClass(), "save"),session);
		Throwable exception = null;
		IProgressService progressService = PlatformUI.getWorkbench().getProgressService();
		try {
			progressService.busyCursorWhile(op);
		} catch (StaleDataException e ) {
			RequirementPlugin.logAndDisplay(new ModelStatus(IStatus.ERROR, "Unable to perform save, due to stale data. Plesase logout and login to refresh data... All changes will unfortunately be lost..."));
			return;
		} catch (RematoException e) {
			exception = e;
		} catch (UndeclaredThrowableException e){
			exception = e.getUndeclaredThrowable();
		} catch (Throwable e) {
			exception = e;
		}
		RequirementPlugin.getInstance().getEditorWindowManager().flushNonPersistedItemsEditors();
		if (null != exception) {
			RequirementPlugin.log(new RematoClientException(new ModelStatus(IStatus.ERROR, "Error when saving.", exception)));
		}
		RequirementPlugin.getInstance().getPreciseSubject().notifyModelUpdated(null);//TODO
		RequirementPlugin.getInstance().getPreciseSubject().notifySessionSaved();
	}
	
	public URL getServerContextUrl() {
		return session.getServerContextUrl();
	}
	
	public List<CommandHistory> retrieveCommandHistory(IModelPersistable persistable) {
		List<CommandHistory> cmdHists = null ;
		try {
			cmdHists = session.retrieveCommandHistory(persistable.getDomainModelObject()) ; //FIXME
//			for (Map map : (List<Map>) cmdhist) {
//				entries.append("Date: " + map.get("date").toString().substring(0, 16) + /*", User: " + cmdHist.getUserName() + */  ", Command: " + map.get("method") + "\n") ;
//
////				for ( Map.Entry entry : (Set<Map.Entry>)map.entrySet() ) {
////					CommandHistory cmdHist = (CommandHistory) entry.getValue() ;
////					entries.append("Date: " + cmdHist.getDate() + ", User: " + cmdHist.getUserName() + ", Command: " + cmdHist.getMethod());
////				}
//			}
		} catch (Exception e) {
			RequirementPlugin.logAndDisplay(new ModelStatus (IStatus.ERROR , "Unable to retrieve history, error occured:" + e.getMessage() , e ) ) ;
//			entries.append("Unable to retrieve history, error occured:" + e.getMessage());
		}
		return cmdHists;
	}
}
